package com.uuah.server.config.spring.parsers;

import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
import org.springframework.beans.factory.xml.ParserContext;
import org.w3c.dom.Element;

import com.uuah.server.config.spring.parsers.assembly.IBeanAssembler;
import com.uuah.server.config.spring.parsers.assembly.configuration.IPropertyConfiguration;
import com.uuah.server.config.spring.parsers.assembly.configuration.ReusablePropertyConfiguration;
import com.uuah.server.config.spring.parsers.assembly.configuration.TempWrapperPropertyConfiguration;
import com.uuah.server.util.spring.SpringXMLUtils;
import com.uuah.utils.StringUtils;

/**
 * This definition parser introduces the notion of Hierarchical processing to
 * nested XML elements. Definition parsers that extend this can refer to parent
 * beans. It does not assume that the parser is restricted to a single property.
 *
 * Calling classes must set the registry at the start of processing.
 *
 * @see com.uuah.server.config.spring.parsers.generic.ChildDefinitionParser
 * @see com.uuah.server.config.spring.parsers.collection.ChildMapEntryDefinitionParser.KeyValuePair
 * @see com.uuah.server.config.spring.parsers.AbstractUuahBeanDefinitionParser
 */
public abstract class AbstractHierarchicalDefinitionParser extends
		AbstractUuahBeanDefinitionParser {

	private ReusablePropertyConfiguration targetPropertyConfiguration = new ReusablePropertyConfiguration(
			new TempWrapperPropertyConfiguration(beanPropertyConfiguration,
					false));
	private BeanDefinition forcedParent = null;

	public IPropertyConfiguration getTargetPropertyConfiguration() {
		return targetPropertyConfiguration;
	}

	protected String getParentBeanName(Element element) {
		return ((Element) element.getParentNode()).getAttribute(ATTRIBUTE_NAME);
	}

	public BeanDefinition getParentBeanDefinition(Element element) {
		if (null != forcedParent) {
			return forcedParent;
		} else {
			String parentBean = getParentBeanName(element);
			if (StringUtils.isBlank(parentBean)) {
				throw new IllegalStateException("No parent for "
						+ SpringXMLUtils.elementToString(element));
			}
			return getRegistry().getBeanDefinition(parentBean);
		}
	}

	/**
	 * The bean assembler gives more reliable/automatic processing of
	 * collections, maps, etc.
	 *
	 * @param element
	 *            The current element
	 * @param bean
	 *            The bean being constructed
	 * @return An assembler that includes Uuah-specific construction logic
	 */
	protected IBeanAssembler getBeanAssembler(Element element,
			BeanDefinitionBuilder bean) {
		BeanDefinition target = getParentBeanDefinition(element);
		return getBeanAssemblerFactory().newBeanAssembler(
				beanPropertyConfiguration, bean, targetPropertyConfiguration,
				target);
	}

	public void forceParent(BeanDefinition parent) {
		forcedParent = parent;
	}

	protected void preProcess(Element element) {
		super.preProcess(element);
		targetPropertyConfiguration.reset();
	}

	// reset the forced parent
	protected void postProcess(ParserContext context, IBeanAssembler assembler,
			Element element) {
		super.postProcess(context, assembler, element);
		forcedParent = null;
	}

}
